package generator_test

import (
	"encoding/json"
	"fmt"
	"github.com/google/go-cmp/cmp"
	"io/ioutil"
	"k8s.io/client-go/kubernetes/scheme"
	"sort"
	"strings"
	"testing"
	"v8s-controller/internal/configuration"

	"v8s-controller/generator"
	"v8s/compose/model"

	"github.com/stretchr/testify/assert"

	v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/runtime"
	k8sjson "k8s.io/apimachinery/pkg/runtime/serializer/json"
)

type (
	GeneratorSuit struct {
		Folder           string
		ImagePullSecrets []string
	}
)

var (
	serializer = k8sjson.NewSerializerWithOptions(k8sjson.DefaultMetaFactory, nil, nil, k8sjson.SerializerOptions{
		Yaml:   true,
		Strict: true,
	})
)

func Test(t *testing.T, suit GeneratorSuit) {
	// Arrange
	gen := generator.NewGenerator(configuration.GeneratorProperties{
		Quota: configuration.NamespaceQuotaProperties{
			CPULimit:              "1000m",
			RAMLimit:              "1024Mi",
			EphemeralStorageLimit: "20Gi",
		},
		Volumes: configuration.VolumesProperties{
			DefaultStorageClassName: "standard",
		},
		ImagePullSecretFiles: suit.ImagePullSecrets,
	})

	input, err := ioutil.ReadFile("generator/" + suit.Folder + "/fixture-mongo.json")
	if err != nil {
		t.Fatalf("failed to read input: %s", err)
	}

	var compose model.Compose
	if err := json.Unmarshal(input, &compose); err != nil {
		t.Fatalf("failed to unmarshall input: %s", err)
	}

	// Act
	resources, err := gen.Generate("v8s-test-ns", compose)
	if err != nil {
		t.Fatalf("failed to generate resources: %s", err)
	}

	// Assert
	sort.SliceStable(resources, comparator(resources))

	builder := &strings.Builder{}

	for _, resource := range resources {
		builder.WriteString("---\n")

		serialized := &strings.Builder{}

		if err := serializer.Encode(resource, serialized); err != nil {
			t.Fatalf("failed to serialize resource: %s", err)
		}

		actual, _, err := scheme.Codecs.UniversalDeserializer().Decode([]byte(serialized.String()), nil, nil)
		if err != nil {
			t.Fatalf("failed to decode actual resource: %s", err)
		}

		if !cmp.Equal(resource, actual) {
			diff := cmp.Diff(resource, actual)
			fmt.Printf(diff + "\n")
			t.Fatalf("resources must be equal")
		}

		builder.WriteString(serialized.String())
	}

	expected, err := ioutil.ReadFile("generator/" + suit.Folder + "/expected-k8s.yaml")
	if err != nil {
		t.Fatalf("failed to read expected: %s", expected)
	}

	assert.Equal(t, strings.TrimSpace(string(expected)), strings.TrimSpace(builder.String()))
}

func comparator(resources []runtime.Object) func(i int, j int) bool {
	return func(i, j int) bool {
		left := resources[i]
		right := resources[j]

		leftKind := left.GetObjectKind().GroupVersionKind().Kind
		rightKind := right.GetObjectKind().GroupVersionKind().Kind
		//log.Printf("leftkind %v, right king %v", leftKind, left.GetObjectKind())
		byKind := strings.Compare(leftKind, rightKind)
		if byKind != 0 {
			return byKind < 0
		}

		leftName := left.(v1.ObjectMetaAccessor).GetObjectMeta().GetName()
		rightName := right.(v1.ObjectMetaAccessor).GetObjectMeta().GetName()

		byName := strings.Compare(leftName, rightName)
		if byName != 0 {
			return byName < 0
		}

		return false
	}
}
